from py2neo import Graph, Node, Relationship, NodeMatcher, RelationshipMatch


class Neo4jMod:
    # 连接neo4j数据库，输入地址、用户名、密码
    def __init__(self, ne4j_url, ne4j_name, ne4j_password, label):
        self.graph = Graph(ne4j_url, username=ne4j_name, password=ne4j_password)
        self.label = label

    def only_one(self):
        # 创建唯一性约束
        self.graph.run("""CREATE CONSTRAINT ON (a:%s) ASSERT a.name IS UNIQUE;""" % self.label)

    # 查看所有的节点数和边数
    def query_all_node_side(self):
        node_count = self.graph.run("""MATCH (n) RETURN count(n);""")
        side_count = self.graph.run("""MATCH ()-->() RETURN count(*);""")
        return node_count, side_count

    # 清空所有数据库
    def del_all_data(self):

        return self.graph.run(
            """MATCH (n)
            OPTIONAL MATCH (n)-[r]-()
            DELETE n,r """)

    # 插入节点
    def insert_node(self, name):
        return self.graph.run(
            """CREATE(n: %s {name: '%s'});""" % (self.label, name)).data()

    # 更新节点 不存在则创建
    def merge_node(self, name):
        return self.graph.run(
            """MERGE (n: %s {name: '%s'});""" % (self.label, name)).data()

    # 插入边 并有边的类别
    def insert_side(self, a_node_name, b_node_name, rela_type, rela_property=None, rela_property_value=None):
        if rela_property:
            type_dic = "{%s : %s}" % (rela_property, rela_property_value)
        else:
            type_dic = ''
        return self.graph.run(
            """MATCH(a: %s), (b:%s)
            WHERE a.name = '%s' AND b.name = '%s'
            CREATE(a) - [: %s %s]->(b);""" % (
                self.label, self.label, a_node_name, b_node_name, rela_type, type_dic))

    # 更新节点 设置新的属性
    def update_node(self, name, new_property, type_name=None):
        if type_name:
            type_name = type_name
        else:
            type_name = name
        if isinstance(new_property, int):
            return self.graph.run(
                """MATCH (n:%s { name: '%s' })
                SET n.%s = %s""" % (self.label, name, type_name, new_property))

        if isinstance(new_property, str):
            return self.graph.run(
                """MATCH (n:%s { name: '%s' })
                SET n.%s = '%s'""" % (self.label, name, type_name, new_property))

    # 删除节点与其相邻的边
    def delete_node_side(self, name):
        return self.graph.run(
            """MATCH (n:%s { name:'%s' })
            DETACH DELETE n;""" % (self.label, name))

    # 删除边
    def delete_side(self, type_name, a_name, b_name):
        # 有顺序a b
        return self.graph.run(
            """MATCH (a:%s)-[r:%s]->(b:%s)
            WHERE a.name = '%s' AND b.name = '%s'
            DELETE r;""" % (self.label, type_name, self.label, a_name, b_name))

    # 查询和一个节点有一层关系的节点
    def query_one_node_side_node(self, name, type_name):

        return self.graph.run(
            """MATCH (:%s { name:'%s' })-[r:%s]->(%s)
            RETURN %s.name;""" % (self.label, name, type_name, self.label, self.label)).data()

    # 查询两个节点之间的关系
    def query_two_node_relations(self, name1, name2):
        # 有顺序的
        return self.graph.run(
            """MATCH (a:%s { name:'%s' })-[r]->(b:%s { name:'%s' })
            RETURN type( r);""" % (self.label, name1, self.label, name2)).data()

    # 查询最短路径
    def query_min_road(self, name1, name2, type_name):
        # 暂未通过
        return self.graph.run(
            """MATCH (ms:%s { name:'%s' }),(cs:%s { name:'%s' }), p = shortestPath((ms)-[r:%s]-(cs))
            RETURN p;""" % (self.label, name1, self.label, name2, type_name)).data()


# 第一版
"""
    # 清空数据库
    def delete_all_data(self):
        self.graph.delete_all()

    # 创建节点
    def creat_node(self, neme_labal, name_name):
        test_node_1 = Node(neme_labal, name=name_name)  # 注意：这里不要加“label=”，否则label会以属性的形式展示，而非标签
        self.graph.create(test_node_1)
        return test_node_1

    # 建立结点关系
    def creat_node_relation(self, name1, name2, relation_type, relation_property=None, relation_property_value=None):
        node_1_call_node_2 = Relationship(name1, relation_type, name2)
        node_1_call_node_2[relation_property] = relation_property_value
        self.graph.create(node_1_call_node_2)
        return node_1_call_node_2

    # 节点/关系的属性赋值以及属性值的更新
    def property_Assignment_update(self, node_1_call_node_2, relation_property, relation_property_value):
        node_1_call_node_2[relation_property] = relation_property_value
        self.graph.push(node_1_call_node_2)

    def match_node(self, n_label, name):
        matcher = NodeMatcher(self.graph)
        match_cur = matcher.match(n_label, name=name)
        return match_cur

    def match_rela(self, nodes=None, r_type=None, order_by=(), skip=None, limit=None):
        matcher = RelationshipMatch(self.graph, nodes=nodes, r_type=r_type, order_by=order_by, skip=skip, limit=limit)
        return matcher
"""
